﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using FluentFTP.Model;
using FluentFTP.View;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.ViewModel;
using Microsoft.Win32;
using FluentFTP.Configuration;
using System.Diagnostics;
using FluentFTP.Dialogs;

namespace FluentFTP.ViewModel
{
    public class MainViewModel : NotificationObject
    {
        private FtpClient client;
        private const string rootPathRegex = "ftp://[^/]+/?";
        private UserSettings settings;

        #region Binding properties

        private string adress;
        public string Adress
        {
            get { return adress; }
            set
            {
                adress = value;
                RaisePropertyChanged(() => Adress);
            }
        }

        private string userName;
        public string UserName
        {
            get { return userName; }
            set
            {
                userName = value;
                RaisePropertyChanged(() => UserName);
            }
        }

        private string password;
        public string Password
        {
            get { return password; }
            set
            {
                password = value;
                RaisePropertyChanged(() => Password);
            }
        }

        private string currentPath;
        public string CurrentPath
        {
            get { return currentPath; }
            set
            {
                //Regex ftpFolderRegex = new Regex(@"ftp:////(\d{1,3}.){3}\d{1,3}(//[^.]*)*");
                //ftpFolderRegex.Ma
                //if (value.Contains(".")) throw new ArgumentException("Current path has to be a folder.");
                // TODO: add regex check

                currentPath = value;
                updateFolders();
                RaisePropertyChanged(() => CurrentPath);
            }
        }

        private ObservableCollection<FtpItem> folders;
        public ObservableCollection<FtpItem> Folders
        {
            get { return folders; }
            set
            {
                folders = value;
                RaisePropertyChanged(() => Folders);
            }
        }

        private bool isConnected;
        public bool IsConnected
        {
            get { return isConnected; }
            set
            {
                isConnected = value;
                RaisePropertyChanged(() => IsConnected);
            }
        }

        private FtpItem selectedItem;
        public FtpItem SelectedItem
        {
            get { return selectedItem; }
            set
            {
                selectedItem = value;
                allCommandsCanExecuteChanged();
            }
        }

        private bool isBusy = false;
        public bool IsBusy
        {
            get { return isBusy; }
            set
            {
                isBusy = value;
                RaisePropertyChanged(() => IsBusy);
            }
        }



        #endregion

        #region FTP commands

        /// <summary>
        /// Fires an action corresponding to FtpItem's type
        /// </summary>
        public DelegateCommand DefaultActionCommand { get; private set; }
        public DelegateCommand ConnectDialogCommand { get; private set; }
        public DelegateCommand<FtpServerInfo> ConnectCommand { get; private set; }
        public DelegateCommand UpCommand { get; private set; }
        public DelegateCommand Reload { get; private set; }
        public DelegateCommand CreateDirectory { get; private set; }

        #endregion

        #region Menu commands

        public DelegateCommand DownloadCommand { get; private set; }
        public DelegateCommand DownloadAsCommand { get; private set; }
        public DelegateCommand OpenUserSettings { get; private set; }
        public DelegateCommand OpenServerSettings { get; private set; }
        public DelegateCommand Upload { get; private set; }

        #endregion

        public MainViewModel()
        {
            settings = Settings.UserSettings;

            initializeFtpCommands();

            initializeMenuCommands();



        }

        private void initializeMenuCommands()
        {
            Func<bool> downloadCanExecute = () => SelectedItem != null && SelectedItem.Type == FtpItemType.File;

            DownloadCommand = new DelegateCommand(
                () => downloadFileAsync(SelectedItem.FullPath),
                downloadCanExecute// TODO: this is not being called.
                );

            DownloadAsCommand = new DelegateCommand(
                () =>
                {
                    string extension = Path.GetExtension(SelectedItem.Name);

                    var saveDialog = new SaveFileDialog();
                    saveDialog.DefaultExt = extension;
                    saveDialog.Filter = string.Format("{0} files | *{0}", extension);
                    saveDialog.InitialDirectory = settings.DefaultSaveFolder;
                    saveDialog.Title = "Select Save Location";
                    saveDialog.ValidateNames = true;
                    if (saveDialog.ShowDialog() != true) return;

                    downloadFileAsync(SelectedItem.FullPath, saveDialog.FileName);
                },
                    downloadCanExecute);

            OpenUserSettings = new DelegateCommand(
                () =>
                {
                    var window = new SettingsView();
                    window.ShowDialog();
                });

            OpenServerSettings = new DelegateCommand(
                () =>
                {
                    var window = new ServersView();
                    window.ShowDialog();
                });
        }

        private void initializeFtpCommands()
        {
            Upload = new DelegateCommand(() =>
                {
                    OpenFileDialog dialog = new OpenFileDialog();
                    dialog.CheckFileExists = true;
                    dialog.Multiselect = false;

                    if (dialog.ShowDialog() == true)
                    {
                        executeAsync(() => client.UploadFile(dialog.FileName, string.Format("{0}/{1}", CurrentPath, Path.GetFileName(dialog.FileName))));
                    }

                }, () => IsConnected);

            Reload = new DelegateCommand(() => CurrentPath = CurrentPath, () => IsConnected);

            ConnectCommand = new DelegateCommand<FtpServerInfo>(serverInfo =>
            {
                this.Adress = serverInfo.Adress;
                this.UserName = serverInfo.UserName;
                this.Password = serverInfo.Password;

                connect();
            });

            ConnectDialogCommand = new DelegateCommand(() =>
            {
                var connectDialog = new ConnectionView(connectionInfo =>
                {
                    this.Adress = connectionInfo.Item1;
                    this.UserName = connectionInfo.Item2;
                    this.Password = connectionInfo.Item3;

                    connect();
                });

                connectDialog.ShowDialog();
            });

            DefaultActionCommand = new DelegateCommand(() =>
            {
                switch (SelectedItem.Type)
                {
                    case FtpItemType.File:
                        downloadFileAsync(SelectedItem.FullPath);
                        break;
                    case FtpItemType.Folder:
                        CurrentPath = SelectedItem.FullPath;
                        break;
                    default:
                        break;
                }
            },
                () => SelectedItem != null);

            UpCommand = new DelegateCommand(() =>
            {
                var rootRegex = new Regex(rootPathRegex);
                var pathSegments = CurrentPath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                StringBuilder upPathBuilder = new StringBuilder();
                for (int i = 0; i < pathSegments.Length - 1; i++)
                {
                    upPathBuilder.Append(pathSegments[i]);
                    upPathBuilder.Append("/");
                    if (i == 0) upPathBuilder.Append("/"); // an extra slash for the 'ftp://'
                }
                upPathBuilder.Remove(upPathBuilder.Length - 1, 1);
                Debug.WriteLine(upPathBuilder.ToString());
                CurrentPath = upPathBuilder.ToString();

            }, () => !isRootPath(CurrentPath));

            CreateDirectory = new DelegateCommand(() =>
                {
                    var dialog = new TextBoxDialog("Enter New Directory Name", directoryName =>
                        {
                            executeAsync(() =>
                                {
                                    client.CreateDirectory(string.Format("{0}/{1}", CurrentPath, directoryName));
                                    updateFolders();
                                });
                        });

                    dialog.ShowDialog();
                }, () => IsConnected);
        }

        private void connect()
        {
            client = new FtpClient(UserName, Password, Adress);
            CurrentPath = Adress;

            IsConnected = true;
        }

        public bool isRootPath(string path)
        {
            if (string.IsNullOrEmpty(path)) return true;

            Regex isRootPathRegex = new Regex(rootPathRegex);
            var matchRoot = isRootPathRegex.Match(path);
            if (matchRoot.Length == path.Length) return true;

            return false;
        }

        private void downloadFileAsync(string pathFrom)
        {
            downloadFileAsync(pathFrom, settings.DefaultSaveFolder + "\\\\" + Path.GetFileName(pathFrom));
        }

        private void downloadFileAsync(string pathFrom, string pathTo)
        {
            executeAsync(() => client.SaveFile(pathFrom, pathTo));
        }


        private void updateFolders()
        {
            executeAsync(() =>
                {
                    client.Url = CurrentPath;
                    Folders = new ObservableCollection<FtpItem>(client.GetDirectoryContents());
                });
        }

        private void allCommandsCanExecuteChanged()
        {
            DownloadCommand.RaiseCanExecuteChanged();
            DownloadAsCommand.RaiseCanExecuteChanged();
            ConnectCommand.RaiseCanExecuteChanged();
            UpCommand.RaiseCanExecuteChanged();
            UpCommand.RaiseCanExecuteChanged();
            Reload.RaiseCanExecuteChanged();
            CreateDirectory.RaiseCanExecuteChanged();
        }

        private void executeAsync(Action action)
        {
            var worker = new BackgroundWorker();
            worker.DoWork += (_, args) =>
                {
                    IsBusy = true;
                    action();
                };

            worker.RunWorkerCompleted += (_, args) =>
                {
                    IsBusy = false;
                    allCommandsCanExecuteChanged();
                };
            worker.RunWorkerAsync();
        }
    }
}
